Skip to content

Conversation

@deanmlittle
Copy link
Contributor

This PR implements the middleware_entrypoint! macro. This macro enables users to define a hot path that skips entrypoint deserialization for high priority instructions, with a fallback to a cold path in the case that it does not return SUCCESS.

Attached is a simple code sample of how it can be used in practice, where an invocation with zero accounts will lead to the hot path, and anything else will fallback to the cold path:

#![cfg_attr(target_os = "solana", no_std)]
use pinocchio::{ProgramResult, account_info::AccountInfo, middleware_entrypoint, msg, no_allocator, nostd_panic_handler, pubkey::Pubkey};

nostd_panic_handler!();
no_allocator!();

middleware_entrypoint!(hot,cold);

// This uses 4 CUs
#[inline(always)]
pub fn hot(input: *mut u8) -> u64 {
    unsafe { *input as u64 }
}

// This uses 113 CUs
#[cold]
#[inline(always)]
pub fn cold(
    _program_id: &Pubkey,
    _accounts: &[AccountInfo],
    _instruction_data: &[u8],
) -> ProgramResult {
    msg!("Hello from my Pinocchio!");
    Ok(())
}

📌 [`middleware_program_entrypoint!`](https://docs.rs/pinocchio/latest/pinocchio/macro.middleware_program_entrypoint.html)

The `middleware_program_entrypoint!` macro defines a dual entrypoint implementation consisting of a `hot` path which bypasses entrypoint deserialization, and a `cold` path with behaves like a regular pinocchio entrypoint that has undergone `program_entrypoint!` deserialization. This gives the user flexibility to implement extreme CU optimizations against the most critical paths in their proggram with the convenience of being able to fallback to a regular pinocchio program to manage the fail case and other instructions that do not require such optimizations.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `middleware_program_entrypoint!` macro defines a dual entrypoint implementation consisting of a `hot` path which bypasses entrypoint deserialization, and a `cold` path with behaves like a regular pinocchio entrypoint that has undergone `program_entrypoint!` deserialization. This gives the user flexibility to implement extreme CU optimizations against the most critical paths in their proggram with the convenience of being able to fallback to a regular pinocchio program to manage the fail case and other instructions that do not require such optimizations.
The `middleware_program_entrypoint!` macro defines a dual entrypoint implementation consisting of a `hot` path which bypasses entrypoint deserialization, and a `cold` path which behaves like a regular pinocchio entrypoint that has undergone `program_entrypoint!` deserialization. This gives the user flexibility to implement extreme CU optimizations against the most critical paths in their program with the convenience of being able to fallback to a regular pinocchio program to manage the fail case and other instructions that do not require such optimizations.

nostd_panic_handler!();
no_allocator!();

middleware_program_entrypoint!(hot,cold);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
middleware_program_entrypoint!(hot,cold);
middleware_program_entrypoint!(hot, cold);

/// nostd_panic_handler!();
/// no_allocator!();
///
/// middleware_program_entrypoint!(hot,cold);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// middleware_program_entrypoint!(hot,cold);
/// middleware_program_entrypoint!(hot, cold);

}
}
};
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
}

@deanmlittle
Copy link
Contributor Author

@febo I have added a feature flag called asm that allows advanced users to consume assembly macros that help them save every last CU on their hot path by only ever manually overriding the return register to an error code in assembly is needed in the hot path. If the hot path does not explicitly exit, the cold path will then be executed directly inline afterwards without the need for a jmp or call instruction, and the resulting numeric type will be converted to a u32 and manually override the value of the return register in assembly accordingly.

Comment on lines +157 to +162
unsafe {
core::arch::asm!(
"mov32 r0, {0}",
in(reg) $value
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a compile-time check that $value is a u32?

Suggested change
unsafe {
core::arch::asm!(
"mov32 r0, {0}",
in(reg) $value
);
}
unsafe {
let v: u32 = $value;
core::arch::asm!(
"mov32 r0, {0}",
in(reg) v
);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants